﻿using System.Globalization;

namespace MDK.Device
{
    public interface IPlc
    {
        bool IsOpened { get; }
        void Open();
        bool BitIsSet(PlcAddress addr);
        void SetBit(PlcAddress addr);
        void ResetBit(PlcAddress addr);
        int ReadInt(PlcAddress addr);
        void WriteInt(PlcAddress addr, int value);
        void Close();
        event CommunicationErrorHandler CommunicationError;
    }

    public class PlcAddress
    {
        public PlcAddress(byte plcMemory, byte dataType, ushort address)
        {
            PlcMemory = plcMemory;
            DataType = dataType;
            Address = address;
        }

        public PlcAddress(string addr)
        {
            if (string.IsNullOrEmpty(addr))
            {
                PlcMemory = 0;
                DataType = 0;
                Address = 0;
            }
            else
            {
                switch (addr[0])
                {
                    case 'X':
                        PlcMemory = PLC_MEMORT_DI;
                        DataType = 0;
                        Address = ushort.Parse(addr.Substring(1), Constants.PlcAddresStyle);
                        break;
                    case 'Y':
                        PlcMemory = PLC_MEMORT_DQ;
                        DataType = 0;
                        Address = ushort.Parse(addr.Substring(1), Constants.PlcAddresStyle);
                        break;
                    case 'M':
                        PlcMemory = PLC_MEMORT_MR;
                        DataType = 0;
                        Address = ushort.Parse(addr.Substring(1), Constants.PlcAddresStyle);
                        break;
                    case 'L':
                        PlcMemory = PLC_MEMORT_LR;
                        DataType = 0;
                        Address = ushort.Parse(addr.Substring(1), Constants.PlcAddresStyle);
                        break;
                    case 'F':
                        PlcMemory = PLC_MEMORT_FR;
                        DataType = 0;
                        Address = ushort.Parse(addr.Substring(1), Constants.PlcAddresStyle);
                        break;
                    case 'D':
                        PlcMemory = PLC_MEMORT_DR;
                        if (addr[1] == 'D')
                        {
                            DataType = PLC_DATATYPE_DINT32;
                            Address = ushort.Parse(addr.Substring(2), Constants.PlcAddresStyle);
                        }
                        else
                        {
                            DataType = PLC_DATATYPE_INT16;
                            Address = ushort.Parse(addr.Substring(1), Constants.PlcAddresStyle);
                        }
                        break;
                    default:
                        PlcMemory = 0;
                        DataType = 0;
                        Address = 0;
                        break;
                }
            }
        }

        public byte PlcMemory { get; private set; }

        public byte DataType { get; private set; }

        public ushort Address { get; private set; }

        private string _addrStr = null;
        public override string ToString()
        {
            if (_addrStr == null)
            {
                var str = Address.ToString("D");
                switch (PlcMemory)
                {
                    case PLC_MEMORT_DI:
                        str = "X" + str;
                        break;
                    case PLC_MEMORT_DQ:
                        str = "Y" + str;
                        break;
                    case PLC_MEMORT_MR:
                        str = "M" + str;
                        break;
                    case PLC_MEMORT_LR:
                        str = "L" + str;
                        break;
                    case PLC_MEMORT_FR:
                        str = "F" + str;
                        break;
                    case PLC_MEMORT_DR:
                        //str = (DataType == PLC_DATATYPE_DINT32 ? "DD" : "D") + str;
                        str = "D" + str;
                        break;
                }
                _addrStr = str;
            }

            return _addrStr;
        }

        public static implicit operator string(PlcAddress address)
        {
            return address.ToString();
        }

        public static implicit operator PlcAddress(string address)
        {
            return new PlcAddress(address);
        }

        public const byte PLC_MEMORT_DI = 1;
        public const byte PLC_MEMORT_DQ = 2;
        public const byte PLC_MEMORT_MR = 3;
        public const byte PLC_MEMORT_LR = 4;
        public const byte PLC_MEMORT_FR = 5;
        public const byte PLC_MEMORT_DR = 6;

        public const byte PLC_DATATYPE_INT16 = 1;
        public const byte PLC_DATATYPE_UINT16 = 2;
        public const byte PLC_DATATYPE_DINT32 = 3;
        public const byte PLC_DATATYPE_HEX32 = 4;
        public const byte PLC_DATATYPE_REAL32 = 5;
        public const byte PLC_DATATYPE_BIN16 = 6;
    }
}
